﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Collections;
using Newtonsoft.Json;

namespace SysSU.JSONRPC
{
    public class JSONRPC2Response : JSONRPC2Message
    {
        private Object result;
        private JSONRPC2Error error;
        private Object id;

        public Object Result
        {
            get { return result; }
            set
            {
                if (value != null
                    && !(value is byte) && !(value is sbyte)
                    && !(value is short) && !(value is ushort)
                    && !(value is int) && !(value is uint)
                    && !(value is long) && !(value is ulong)
                    && !(value is float) && !(value is double)
                    && !(value is String) && !(value is IList) && !(value is IDictionary))
                {
                    throw new ArgumentException("The result must map to a JSON type");
                }

                result = value;
                error = null;
            }
        }

        public JSONRPC2Error Error
        {
            get { return error; }
            set
            {
                if (value == null)
                {
                    throw new ArgumentException("The error object cannot be null");
                }
                error = value;
                result = null;
            }
        }

        public Object Id
        {
            get { return id; }
            set
            {
                verifyType(value);
                id = value;
            }
        }

        public static new JSONRPC2Response Parse(String jsonString) 
        {
            var json = JSONRPC2Message.ParseJSONObject(jsonString);

            ensureVersion2(json["jsonrpc"], jsonString);

            var id = json["id"];
            verifyType(id);

            if (json["result"] != null && json["error"] == null)
            {
                var res = JsonConvert.DeserializeObject(json["result"].ToString());
                return new JSONRPC2Response() { Result = res, Id = id };
            }
            else if (json["result"] == null && json["error"] != null) 
            {
                var errorJSON = json["error"] as Dictionary<string, object>;
                if (errorJSON == null) 
                {
                    throw new JSONRPC2InvalidMessageException(
                            "Invalid JSON-RPC 2.0 response: Missing error object", jsonString);
                }
            
                int errorCode;
                try 
                {
                    errorCode = (int)errorJSON["code"];
                } catch
                {
                    throw new JSONRPC2InvalidMessageException(
                            "Invalid JSON-RPC 2.0 response: Error code missing or not an integer",
                            jsonString);
                }

                string errorMessage = null;
                try 
                {
                    errorMessage = errorJSON["message"] as string;
                } catch
                {
                    throw new JSONRPC2InvalidMessageException(
                            "Invalid JSON-RPC 2.0 response: Error message missing or not a string",
                            jsonString);
                }

                var errorData = errorJSON["data"];

                return new JSONRPC2Response() 
                    { Error = new JSONRPC2Error(errorCode, errorMessage, errorData), Id = id };
            }
            else if (json["result"] != null && json["error"] != null) 
            {
                throw new JSONRPC2InvalidMessageException(
                        "Invalid JSON-RPC 2.0 response: You cannot have result and error at the same time"
                        , jsonString);
            }
            else if (json["result"] == null && json["error"] == null) 
            {
                throw new JSONRPC2InvalidMessageException(
                        "Invalid JSON-RPC 2.0 response: Neither result nor error specified"
                        , jsonString);
            } 
            else 
            {
                throw new Exception("Assertion error.");
            }        
        }


        public override JObject ToJSON()
        {
            var json = new JObject();

            if (error != null) 
            {
                json["error"] = error.ToJSON();
            } 
            else 
            {
                json["result"] = JObject.Parse(JsonConvert.SerializeObject(result));
            }
            json["id"] = JObject.Parse(JsonConvert.SerializeObject(id));
            json["jsonrpc"] = "2.0";

            return json;
        }
    }
}
